// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Jeff Stewart
//
// The Executive creates and executes the probes.

#include "probes/executive.h"
#include "probes/localhost.h"
#include "probes/popprobe.h"
#include "probes/imapprobe.h"
#include "probes/smtpprobe.h"
#include "ui/reportgen.h"

namespace popdiag {

Executive::Executive(QObject *parent, const ExecutiveOptions &options)
    : QObject(parent),
      reporter_(NULL),
      current_probe_(NULL),
      options_(options),
      report_(),
      probes_(NULL),
      abort_(false),
      progress_completed_(0),
      progress_estimated_total_(0) {
}

Executive::~Executive() {
}

ProbeSet *Executive::probes() {
  return probes_.get();
}

void Executive::Abort() {
  abort_ = true;
}

void Executive::Start() {
  CHECK(current_probe_ == NULL);
  CHECK(probes_.get() == NULL);

  // Qt-owned by this.
  reporter_ = new ReportGenerator(this);
  reporter_->SetUsername(options_.username);
  reporter_->AddConfigInfo(options_.config_log);

  probes_.reset(new ProbeSet(NULL));
  LocalHostProbe *local_probe = new LocalHostProbe();
  // Probes_ takes ownership of local_probe.
  probes_->AddProbe(local_probe);
  reporter_->AddProbe("Local Host Information", local_probe);

  if (options_.imap_server != "") {
    ImapProbeFactory *imap_factory = new ImapProbeFactory(
        options_.username,
        options_.password,
        options_.imap_server,
        options_.pull_all_mail);
    Probe *imap_probe = new MultiProbe("imap", options_.imap_server,
                                     imap_factory);
    // Probes_ takes ownership of imap_probe.
    probes_->AddProbe(imap_probe);
    reporter_->AddProbe("Test IMAP Session", imap_probe);
  }

  if (options_.pop_server != "") {
    PopProbeFactory *pop_factory = new PopProbeFactory(
        options_.username,
        options_.password,
        options_.pop_server,
        options_.pull_all_mail);
    Probe *pop_probe = new MultiProbe("pop", options_.pop_server, pop_factory);
    // Probes_ takes ownership of pop_probe.
    probes_->AddProbe(pop_probe);
    reporter_->AddProbe("Test POP Session", pop_probe);
  }

  if (options_.smtp_server != "") {
    SmtpProbeFactory *smtp_factory = new SmtpProbeFactory(
        options_.username,
        options_.password,
        options_.smtp_server,
        options_.smtp_port);
    Probe *smtp_probe = new MultiProbe("smtp", options_.smtp_server,
                                       smtp_factory);
    // Probes_ takes ownership of smtp_probe.
    probes_->AddProbe(smtp_probe);
    reporter_->AddProbe("Test SMTP Session", smtp_probe);
  }

  // Ensure that NextProbe() gets the first probe we scheduled.
  probes_->Rewind();

  // Ensure that the progress indicator starts at zero if this instance
  // of the executive is called multiple times.
  UpdateProgress();
  QueueNextProbe();
}

void Executive::QueueNextProbe() {
  CHECK(current_probe_ == NULL);
  current_probe_ = probes_->NextProbe();
  if (NULL == current_probe_) {
    ProbesDone();
    return;
  }
  if (abort_) {
    return;
  }
  emit ProbeStarted(current_probe_);
  CHECK(connect(current_probe_, SIGNAL(ProgressMade(int, int)),
                this, SLOT(UpdateProgress())));
  CHECK(connect(current_probe_, SIGNAL(Complete()),
                this, SLOT(ProbeDone())));
  current_probe_->Start();
}

void Executive::ProbeDone() {
  emit ProbeCompleted(current_probe_);
  current_probe_ = NULL;
  QueueNextProbe();
}

void Executive::ProbesDone() {
  report_ = reporter_->GenerateReport();
  emit ReportReady();
  UpdateProgress();
  emit Done();
}

void Executive::UpdateProgress() {
  int total = 0;
  int completed = 0;
  ProbeSet::Storage probes;
  probes_->GetProbes(&probes);
  for (ProbeSet::Storage::iterator i = probes.begin();
       i != probes.end(); i++) {
    int probe_completed = 0;
    int probe_estimated_total = 0;
    (*i)->GetProgress(&probe_completed, &probe_estimated_total);
    ExecuteStatus es = (*i)->execute_status();
    // Make sure the probes are emitting valid progress and total values
    // based on their execution status. Normalize the total of each probe
    // to 1000 since they have different actual scales.
    switch (es) {
      case FINISHED:
        probe_completed = probe_estimated_total = 1000;
        break;
      case NOT_EXECUTED:
        CHECK_EQ(probe_completed, 0);
        CHECK_GE(probe_estimated_total, 0);
        probe_completed = 0;
        probe_estimated_total = 1000;
        break;
      case RUNNING:
        CHECK_GE(probe_estimated_total, probe_completed);
        probe_completed = 1000.0 * probe_completed / probe_estimated_total;
        probe_estimated_total = 1000;
        break;
    }
    total += probe_estimated_total;
    completed += probe_completed;
  }
  if (completed != progress_completed_ ||
      total != progress_estimated_total_) {
    progress_completed_ = completed;
    progress_estimated_total_ = total;
    emit ProgressMade(progress_completed_, progress_estimated_total_);
  }
}

void Executive::GetProgress(int *completed, int *estimated_total) {
  *completed = progress_completed_;
  *estimated_total = progress_estimated_total_;
}

}  // namespace popdiag
